﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Yoozoo.Gameplay.RTS
{
    public class GpuInstanceGroup
    {
        public List<GPUInstance_SpawnObject> spawnObjects = new List<GPUInstance_SpawnObject>();
        private MaterialPropertyBlock properties  = new MaterialPropertyBlock();
        private int m_layer = 0;

        private static int initCount = 30;
        
        private Material mat;
        private Mesh mesh;
        private Material shadowMat;
        private Matrix4x4[] matrices = new Matrix4x4[initCount];
        private float[] shadowHeight = new float[initCount];
        private Vector4[] shadowColors = new Vector4[initCount];
        int _ShadowHeight = Shader.PropertyToID("_ShadowHeight");
        int _ShadowColor = Shader.PropertyToID("_ShadowColor");

        private int instanceCount = 0;
        public void AddData(int configId,Material mat,Mesh mesh,Material shadowMat)
        {
            this.mat = mat;
            this.mesh = mesh;
            this.shadowMat = shadowMat;
        }

        public GPUInstance_SpawnObject AddSpawnObject(Transform transform)
        {
            m_layer = transform.gameObject.layer;
            var newObject = new GPUInstance_SpawnObject();
            newObject.Initial(transform);
            newObject.indexInArray = spawnObjects.Count;
            spawnObjects.Add(newObject);
            instanceCount++;
            if (instanceCount > matrices.Length)
            {
                float growthfactor = matrices.Length < 50 ? 1.5f : 1.3f;
                int count = Math.Max(instanceCount, Mathf.FloorToInt(matrices.Length * growthfactor));
                matrices = new Matrix4x4[count];
                shadowHeight = new float[count];
                shadowColors = new Vector4[count];
            }
            return newObject;
        }

        public void RemoveSpawnObject(GPUInstance_SpawnObject spawnObject)
        {
            var indexInArray = spawnObject.indexInArray;
            if (indexInArray < spawnObjects.Count - 1)
            {
                var lastObject = spawnObjects[spawnObjects.Count - 1];
                lastObject.indexInArray = indexInArray;
                spawnObjects[indexInArray] = lastObject;
            }
            spawnObjects.RemoveAt(spawnObjects.Count - 1);
            instanceCount--;
        }
        
        public void Update()
        {
            if (!SystemInfo.supportsInstancing)
            {
                return;
            }
            if (instanceCount == 0)
            {
                return;
            }
            for (int i = 0; i < spawnObjects.Count; i++)
            {
                spawnObjects[i].Update();
                matrices[i] = spawnObjects[i].localToWorldMatrix;
                shadowHeight[i] = spawnObjects[i].shadowHeight;
                shadowColors[i] = new Vector4(0,0,0,spawnObjects[i].shadowAlpha);
            }
            properties.SetFloatArray(_ShadowHeight,shadowHeight);
            properties.SetVectorArray(_ShadowColor,shadowColors);
            Graphics.DrawMeshInstanced(mesh,0,mat,matrices,instanceCount,properties, UnityEngine.Rendering.ShadowCastingMode.Off, false, m_layer);
            if (GPUInstancingManager.IsShadowActive)
            {
                Graphics.DrawMeshInstanced(mesh,0,shadowMat,matrices,instanceCount,properties, UnityEngine.Rendering.ShadowCastingMode.Off, false, m_layer);
            }
        }

        public void SetLayer(int layer)
        {
            m_layer = layer;
        }

        public void Dispose()
        {
            matrices = null;
            spawnObjects?.Clear();
            spawnObjects = null;
            mat = null;
            mesh = null;
            shadowMat = null;
        }
    }
}